package org.xtext.runtime.plan;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.xtext.runtime.formula.IFormula;
import org.xtext.runtime.interpreter.Agent;
import org.xtext.runtime.term.ITerm;
import org.xtext.runtime.term.Term;
import org.xtext.runtime.term.Variable;
import org.xtext.runtime.util.Bindings;
import org.xtext.runtime.util.EvaluateTermsVisitor;

public class WaitStep implements IPlanStep {
	private IFormula guard;
	private ITerm timeout;
	private long timeoutValue = -1;
	
	public WaitStep(IFormula guard) {
		this.guard = guard;
	}
	
	public WaitStep(IFormula guard, ITerm timeout) {
		this.guard = guard;
		this.timeout = timeout;
	}
	
	@Override
	public WaitStep apply(Bindings set) {
		// need to check this as i re-added it...
		guard = guard.apply(set);
		if (timeout != null) timeout = timeout.apply(set);
		return this;
	}

	public String toString() {
		return toString("");
	}
	
	public String toString(String tabs) {
		if (timeout != null) return tabs + "wait (" + guard + "," + timeout + ")";
		return tabs + "wait (" + guard + ")";
	}

	public String toFullString(String tabs) {
		return tabs + "wait (" + guard + ")";
	}

	public IFormula guard() {
		return guard;
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public void handle(Agent agent, PlanStack planStack) {
		List<Bindings> result = agent.query((IFormula) guard.accept(new EvaluateTermsVisitor(agent)));
		if (result == null) {
			if (timeout != null) {
				if (timeoutValue == -1) {
					int value = ((Term<Integer>) timeout.accept(new EvaluateTermsVisitor(agent))).value();
					timeoutValue = System.currentTimeMillis() + value;
				} else {
					if (System.currentTimeMillis() > timeoutValue) {
						agent.logAction(toString() + " [TIMEOUT Occurred]");
						planStack.fail();
						return;
					}
				}
			}
			planStack.back();
		} else {
			if (!result.isEmpty()) { 
				guard.apply(result.get(0));
			}
			agent.logAction(toString() + " [SUCCESS]");
		}
	}

	@Override
	public Set<Variable> variables() {
		Set<Variable> variables = new HashSet<Variable>();
		variables.addAll(guard.variables());
		if (timeout != null && Variable.class.isInstance(timeout) ) variables.add((Variable) timeout);
		return variables;
	}

	@Override
	public WaitStep copy(Map<String, Variable> vars) {
		if (timeout != null) return new WaitStep(guard.copy(vars), timeout.copy(vars));
		return new WaitStep(guard.copy(vars));
	}

	@Override
	public void reset() {
	}
}
